/**
 * LMK0482X: TI-LMK0482X-时钟抖动消除器
 */
 
#include "share/std_err.h"
#include "share/unios.h"
#include "lmk0482x.h"

#define REGADDR_ID_DEVICE_TYPE  0x003
#define REGADDR_ID_PROD_MSB     0x004
#define REGADDR_ID_PROD_LSB     0x005
#define REGADDR_ID_MASKREV      0x006
#define REGADDR_ID_VNDR_MSB     0x00C
#define REGADDR_ID_VNDR_LSB     0x00D

static std_err_t _regw(unidrv_lmk0482x_t *dev, uint16_t regaddr, uint8_t regval) {
	uint8_t tx[3], rx[3];
    // 注意：big-endian(大端在前)
    tx[0] = (regaddr >> 8 ) & 0xFF;
    tx[1] = (regaddr      ) & 0xFF;
    tx[2] = regval;
    
    dev->nss(0); //{{{
    std_err_t err = mcu_spi_trx(dev->spi, tx, rx, 3, NULL);
    dev->nss(1); //}}}
    return err;
}

static uint8_t _regr(unidrv_lmk0482x_t *dev, uint16_t regaddr) {
	uint8_t tx[3], rx[3];
    // 注意：big-endian(大端在前)
    tx[0] = ((regaddr >> 8) & 0xFF) | 0x80; // R(1)/W(0) 标志设置为 1 表示读寄存器
    tx[1] = regaddr & 0xFF;
    tx[2] = 0xFF;
    
    dev->nss(0); //{{{
    mcu_spi_trx(dev->spi, tx, rx, 3, NULL);
    dev->nss(1); //}}}
    return rx[2];
}

static std_err_t _spi_reg_write(unidrv_lmk0482x_t *dev, uint32_t regval) {
	uint8_t tx[3], rx[3];
    // 注意：只发低 24 位，并且要 big-endian(大端在前)
    tx[0] = (regval >> 16) & 0xFF;
    tx[1] = (regval >> 8 ) & 0xFF;
    tx[2] = (regval      ) & 0xFF;
    
    dev->nss(0); //{{{
    std_err_t err = mcu_spi_trx(dev->spi, tx, rx, 3, NULL);
    dev->nss(1); //}}}
    return err;
}

std_err_t unidrv_lmk0482x_init(
    unidrv_lmk0482x_t *dev,
    uint32_t regs1[],
    uint32_t regs2[],
    uint32_t regs3[],
    uint32_t regs5[],
    uint32_t regs6[] )
{
    if(!dev || !dev->spi || !dev->nss || !dev->rst_out || !dev->sync_out || !dev->ld1_in || !dev->ld2_in || !regs1 || !regs2 || !regs3 || !regs5 || !regs6) return STD_ERR_INVALID_ARG;
    std_err_t err = STD_ERR_OK;
        
    // 复位信号 拉高 -> 拉低，实现对 LMK0482X 的硬复位控制
    dev->rst_out(1); unios_us_delay(1000); dev->rst_out(0); unios_us_delay(1000);    
    //_regw(dev, 0x000, 0x90); unios_us_delay(1000); // 复位并设置 SPI 为 4 线模式
    _regw(dev, 0x000, 0x10); // 设置 SPI 为 4 线模式
    _regw(dev, 0x148, 0x33); // CLKin_SEL0 引脚设置为: SPI readback, Output (push-pull)
    _regw(dev, 0x002, 0x00); // POWERDOWN=0
    
    // 检查芯片类型
    if(_regr(dev, REGADDR_ID_DEVICE_TYPE) != LMK0482X_ID_DEVICE_TYPE) {
        return STD_ERR_INVALID_ID;
    }
    
    // 检查芯片PID
    uint16_t pid   = _regr(dev, REGADDR_ID_PROD_MSB);
    pid = (pid<<8) | _regr(dev, REGADDR_ID_PROD_LSB);
    if(pid != LMK0482X_ID_PROD) return STD_ERR_INVALID_ID;
    
    // 检查芯片版本
    dev->id_maskrev = _regr(dev, REGADDR_ID_MASKREV);
    
    // 检查芯片VID
    uint16_t vid   = _regr(dev, REGADDR_ID_VNDR_MSB);
    vid = (vid<<8) | _regr(dev, REGADDR_ID_VNDR_LSB);
    if(vid != LMK0482X_ID_VNDR) return STD_ERR_INVALID_ID;

    for(int i=0; regs1[i] != LMK0482X_REG_END; i++) {
        _spi_reg_write(dev, regs1[i]);
        unios_us_delay(1000);
    }
    unios_us_delay(10000);

    for(int i=0; regs2[i] != LMK0482X_REG_END; i++) {
        _spi_reg_write(dev, regs2[i]);
        unios_us_delay(1000);
    }
    unios_us_delay(10000);

    for(int i=0; regs3[i] != LMK0482X_REG_END; i++) {
        _spi_reg_write(dev, regs3[i]);
        unios_us_delay(1000);
    }
    unios_us_delay(10000);

    // SYNC 信号 拉高 -> 拉低，实现对 LMK0482X 的 同步 控制
    dev->sync_out(1); unios_us_delay(10000);
    dev->sync_out(0); unios_us_delay(10000);
    unios_us_delay(10000);

    for(int i=0; regs5[i] != LMK0482X_REG_END; i++) {
        _spi_reg_write(dev, regs5[i]);
        unios_us_delay(1000);
    }
    unios_us_delay(10000);

    for(int i=0; regs6[i] != LMK0482X_REG_END; i++) {
        _spi_reg_write(dev, regs6[i]);
        unios_us_delay(1000);
    }
    unios_us_delay(10000);

    // 等待 PLL 锁定
    uint32_t ret; // =0 表示等待超时；>0 表示正常，值为等待的时间    
    unios_wait_ms_while(ret, 10000, (dev->ld1_in() == 0)); // 等待 LD1 为高(PLL1 锁定)，见寄存器 0x15F 配置
    if(ret == 0) return STD_ERR_TIMEOUT;    
    unios_wait_ms_while(ret, 10000, (dev->ld2_in() == 0)); // 等待 LD2 为高(PLL2 锁定)，见寄存器 0x16E 配置
    if(ret == 0) return STD_ERR_TIMEOUT;
    
	return err;
}
